import * as THREE from '../../build/three.module.js';
import { GUI } from '../../jsm/libs/dat.gui.module.js';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';
import { TeapotBufferGeometry } from '../../jsm/geometries/TeapotBufferGeometry.js';

const effectController = {
  shininess: 40,
  ka: .17,
  kd: .51,
  ks: .2,
  metallic: true,

  hue: .121, // 色度
  saturation: .73, // 饱和度
  lightness: .66, // 亮度

  lhue: .04,
  lsaturation: .01, // 非0时才显示分数
  llightness: 1,

  lx: .32,
  ly: .39,
  lz: .7,
  newTess: 15,
  bottom: true,
  lid: true,
  body: true,
  fitLid: false,
  nonblim: false,
  newShading: 'glossy'
}

let teapot;
const teapotSize = 400;
let tess = -1,
    bBottom,
    bLid,
    bBody,
    bFitLid,
    bNonBlim,
    shading;

const diffuseColor = new THREE.Color(); // 漫反射颜色
const specularColor = new THREE.Color(); // 镜面反射颜色

const scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);

const camera = new THREE.PerspectiveCamera(65, innerWidth / innerHeight, 1, 10000);
camera.position.set(-600, 550, 1300);

const ambientLight = new THREE.AmbientLight(0x333333);

const light = new THREE.DirectionalLight(0xffffff, 1);
// 在GUI中设置光照

const textureMap = new THREE.TextureLoader().load('../../textures/uv_grid_opengl.jpg');
textureMap.wrapS = textureMap.wrapT = THREE.RepeatWrapping;
textureMap.anisotropy = 16; // 各项异性过滤

// 反射的环境贴图
const path = '../../textures/park3Med/';
const urls = [
  path + 'px.jpg',
  path + 'nx.jpg',

  path + 'py.jpg',
  path + 'ny.jpg',

  path + 'pz.jpg',
  path + 'nz.jpg'
]
const textrueCube = new THREE.CubeTextureLoader().load(urls);

// 材质
const mtlColor = new THREE.Color();
mtlColor.setRGB(1, 1, 1);

const wireMtl = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
const flatMtl = new THREE.MeshPhongMaterial({ color: mtlColor, specular: 0x000000, flatShading: true, side: THREE.DoubleSide });
const gouraudMtl = new THREE.MeshLambertMaterial({ color: mtlColor, side: THREE.DoubleSide });
const phongMtl= new THREE.MeshPhongMaterial({ color: mtlColor, side: THREE.DoubleSide });
const textureMtl = new THREE.MeshPhongMaterial({ color: mtlColor, map: textureMap, side: THREE.DoubleSide });
const relectiveMtl = new THREE.MeshPhongMaterial({ color: mtlColor, envMap: textrueCube, side: THREE.DoubleSide });

scene.add(ambientLight);
scene.add(light);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
renderer.gammaInput = true;
renderer.gammaOutput = true;
document.body.appendChild(renderer.domElement);

// 一旦茶壶配置发生变化，场景就要重新构建
const createNewTeapot = () => {
  if (teapot !== undefined) {
    teapot.geometry.dispose();
    scene.remove(teapot);
  }

  const teapotGmt = new TeapotBufferGeometry(teapotSize,
    tess,
    effectController.bottom,
    effectController.lid,
    effectController.body,
    effectController.fitLid,
    ! effectController.nonblim)

  teapot = new THREE.Mesh(
    teapotGmt,
    shading === 'wireframe' ? wireMtl : (
      shading === 'flat' ? flatMtl : (
        shading === 'smooth' ? gouraudMtl : (
          shading === 'glossy' ? phongMtl : (shading === 'textured' ? textureMtl : relectiveMtl))))
  )

  scene.add(teapot);
}

// 渲染函数
const render = () => {
  if ( effectController.newTess !== tess ||
    effectController.bottom !== bBottom ||
    effectController.lid !== bLid ||
    effectController.body !== bBody ||
    effectController.fitLid !== bFitLid ||
    effectController.nonblinn !== bNonBlim ||
    effectController.newShading !== shading ) {

    tess = effectController.newTess;
    bBottom = effectController.bottom;
    bLid = effectController.lid;
    bBody = effectController.body;
    bFitLid = effectController.fitLid;
    bNonBlim = effectController.nonblim;
    shading = effectController.newShading;

    createNewTeapot();
  }

  phongMtl.shininess = effectController.shininess;
  textureMtl.shininess = effectController.shininess;

  diffuseColor.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
  if (effectController.metallic) {
    // 让颜色看起来更金属一点
    specularColor.copy(diffuseColor);
  } else {
    // 颜色更塑料一点
    specularColor.setRGB(1, 1, 1);
  }

  diffuseColor.multiplyScalar(effectController.kd);
  flatMtl.color.copy(diffuseColor);
  gouraudMtl.color.copy(diffuseColor);
  phongMtl.color.copy(diffuseColor);
  textureMtl.color.copy(diffuseColor);

  specularColor.multiplyScalar(effectController.ks);
  phongMtl.specular.copy(specularColor);
  textureMtl.specular.copy(specularColor);

  // 设置环境光
  ambientLight.color.setHSL(effectController.hue, effectController.saturation, effectController.lightness * effectController.ka);

  // 设置平行光
  light.position.set(effectController.lx, effectController.ly, effectController.lz);
  light.color.setHSL(effectController.lhue, effectController.lsaturation, effectController.llightness);

  // 背景空间与茶杯分开渲染，一直在茶杯之后
  if (shading === 'reflective') {
    scene.background = textrueCube;
  } else {
    scene.background = null;
  }

  renderer.render(scene, camera);
}

const control = new OrbitControls(camera, renderer.domElement);
control.addEventListener('change', render);

// GUI
const gui = new GUI();
let h = gui.addFolder('材质控制');

// GUI 材质属性
h.add(effectController, 'shininess', 1, 400, 1).name('光泽度').onChange(render);
h.add(effectController, 'kd', 0, 1, .025).name('扩散强度').onChange(render);
h.add(effectController, 'ks', 0, 1, .025).name('镜面反射强度').onChange(render);
h.add(effectController, 'metallic').name('金属质感').onChange(render);

// GUI 材质颜色
h = gui.addFolder('材质颜色');
h.add(effectController, 'hue', 0, 1, .025).name('色度').onChange(render);
h.add(effectController, 'saturation', 0, 1, .025).name('饱和度').onChange(render);
h.add(effectController, 'lightness', 0, 1, .025).name('亮度').onChange(render);

// GUI 光源属性
h = gui.addFolder('光源属性');
h.add(effectController, 'lhue', 0, 1, .025).name('光源色度').onChange(render);
h.add(effectController, 'lsaturation', 0, 1, .025).name('光源饱和度').onChange(render);
h.add(effectController, 'llightness', 0, 1, .025).name('光源亮度').onChange(render);
h.add(effectController, 'ka', 0, 1, .025).name('环境光扩散').onChange(render);

// GUI 光源位置
h = gui.addFolder('光源方向');
h.add(effectController, 'lx', -1, 1, .025).name('x').onChange(render);
h.add(effectController, 'ly', -1, 1, .025).name('y').onChange(render);
h.add(effectController, 'lz', -1, 1, .025).name('z').onChange(render);

// GUI 部分渲染控制
h = gui.addFolder('细分渲染');
h.add(effectController, 'newTess', [ 2, 3, 4, 5, 6, 8, 10, 15, 20, 30, 40, 50 ]).name('分片等级').onChange(render); // 精粗模
h.add(effectController, 'lid').name('显示壶盖').onChange(render);
h.add(effectController, 'body').name('显示壶身').onChange(render);
h.add(effectController, 'bottom').name('显示壶底').onChange(render);
h.add(effectController, 'fitLid').name('满盖').onChange(render);
h.add(effectController, 'nonblim').name('原尺寸').onChange(render);

// GUI 底纹
gui.add(effectController, 'newShading', [ "wireframe", "flat", "smooth", "glossy", "textured", "reflective" ]).name('底纹').onChange(render);

render();

const handleWindowResize = () => {
  renderer.setSize(innerWidth, innerHeight);

  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  render();
}
window.addEventListener('resize', handleWindowResize, false);